package com.excel.read;

import java.lang.reflect.*;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class BaseExcelCallBack<T> implements ExcelCallBack<T> {

    private String[] columns;

    private Map<String, String> params = new HashMap<>();

    public BaseExcelCallBack() {
        this.columns = new String[]{};
    }

    public BaseExcelCallBack(String[] columns) {
        this.columns = columns;
    }

    @Override
    public void readLine(int row, int column, String value, Map<String, String> map) {
        System.err.println(column + "---" + value);
        if (columns.length > column) {
            map.put(columns[column], value);
        }
    }

    @Override
    public void readHeader(int row, int column, String value, Map<String, String> map) {
        System.err.println(column + "---" + value);
        if (columns.length > column) {
            map.put(columns[column], value);
        }
    }

    @Override
    public T read(Map<String, String> map) {
        try {
            Type genericSuperclass = getClass().getGenericSuperclass();
            if (genericSuperclass instanceof ParameterizedType) {
                ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                Type typeArgument = actualTypeArguments[0];
                if (typeArgument instanceof Class) {
                    Class<?> aClass = (Class<?>) typeArgument;
                    Constructor<?> constructor = aClass.getConstructor();
                    T t = (T) constructor.newInstance();
                    for (Map.Entry<String, String> entry : map.entrySet()) {
                        String key = entry.getKey();
                        String value = entry.getValue();
                        Field field = aClass.getDeclaredField(key);
                        typecast(field, t, value);
                    }
                    return t;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ExcelCallBack.super.read(map);
    }

    public Date formatDate(String date) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {
            Date parse = sdf.parse(date);
            return parse;
        } catch (ParseException e) {
            return new Date();
        }
    }

    public Long formatLong(String value) {
        return Long.valueOf(value);
    }

    public Float formatFloat(String value) {
        return Float.valueOf(value);
    }

    public Double formatDouble(String value) {
        return Double.valueOf(value);
    }

    public Integer formatInteger(String value) {
        int index = value.indexOf(".");
        if (index > 0) {
            value = value.substring(0, index);
        }
        return Integer.parseInt(value);
    }

    public void typecast(Field field, Object obj, Object value) {
        Class<?> type = field.getType();
        Object invokeValue = value;
        try {
            field.setAccessible(true);
            String typeName = type.getTypeName();
            if (typeName.equals(String.class.getTypeName())) {
                field.set(obj, invokeValue);
                return;
            }
            if (typeName.equals(Date.class.getTypeName())) {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                invokeValue = sdf.parse(String.valueOf(value));
                field.set(obj, invokeValue);
                return;
            }

            if (typeName.equals(int.class.getTypeName())) {
                type = Integer.class;
            }
            if (typeName.equals(float.class.getTypeName())) {
                type = Float.class;
            }
            if (typeName.equals(double.class.getTypeName())) {
                type = Double.class;
            }
            Method method = type.getDeclaredMethod("valueOf", String.class);
            if (method != null) {
                method.setAccessible(true);
                invokeValue = method.invoke(type, value);
                method.setAccessible(false);
            }
            field.set(obj, invokeValue);
            field.setAccessible(false);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void add(String key, String value) {
        params.put(key, value);
    }

    @Override
    public Map<String, String> getParams() {
        return params;
    }
}
